package com.tzu.utils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 字符串工具类<br>
 *
 * @author braveheartwzm
 */
public class StringTools {

    public static final String UTF8 = "UTF-8";

    /**
     * 分号
     */
    public static final String SPLIT_DEFAULT = ";";
    /**
     * 逗号
     */
    public static final String SPLIT_COMMA = ",";

    /**
     * 0~F的char数组，数组长度为10
     */
    public static final char[] RANDOM_NUM = {'0', '1', '2', '3', '4', '5', '6',
            '7', '8', '9'};

    /**
     * 0~F的char数组，数组长度为16
     */
    public static final char[] RANDOM_HEX = {'0', '1', '2', '3', '4', '5', '6',
            '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

    /**
     * 0~9,a~z,A~F的char数组，数组长度62
     */
    public static final char[] RANDOM_ALL = {'0', '1', '2', '3', '4', '5', '6',
            '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
            'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
            'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
            'X', 'Y', 'Z'};

    public static final String BLANK_STRING = "";

    private StringTools() {
    }

    /**
     * 获取hash字符串
     */
    public static String getRandomStr(int length, char[] HASH_ARRAY) {
        StringBuilder val = new StringBuilder();
        for (int i = 0; i < length; i++) {
            val.append(HASH_ARRAY[(int) (Math.random() * HASH_ARRAY.length)]);
        }
        return val.toString();
    }

    /**
     * 获取hash数字
     */
    public static String getRandomNum(int length) {
        return getRandomStr(length, RANDOM_NUM);
    }

    /**
     * 获取16进制hash值
     *
     * @param length hash值长度
     */
    public static String getRandomHex(int length) {
        return getRandomStr(length, RANDOM_HEX);
    }

    /**
     * 获取hash值，全字段
     *
     * @param length hash值长度
     */
    public static String getRandomAll(int length) {
        return getRandomStr(length, RANDOM_ALL);
    }

    /**
     * <p>
     * Checks if a String is whitespace, empty ("") or null.
     * </p>
     *
     * <pre>
     * StringUtils.isBlank(null)      = true
     * StringUtils.isBlank("")        = true
     * StringUtils.isBlank(" ")       = true
     * StringUtils.isBlank("bob")     = false
     * StringUtils.isBlank("  bob  ") = false
     * </pre>
     *
     * @param str the String to check, may be null
     * @return <code>true</code> if the String is null, empty or whitespace
     * @since 2.0
     */
    public static boolean isBlank(String str) {
        int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if ((Character.isWhitespace(str.charAt(i)) == false)) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if a String is not empty (""), not null and not whitespace only.
     * </p>
     *
     * <pre>
     * StringUtils.isNotBlank(null)      = false
     * StringUtils.isNotBlank("")        = false
     * StringUtils.isNotBlank(" ")       = false
     * StringUtils.isNotBlank("bob")     = true
     * StringUtils.isNotBlank("  bob  ") = true
     * </pre>
     *
     * @param str the String to check, may be null
     * @return <code>true</code> if the String is not empty and not null and not
     * whitespace
     * @since 2.0
     */
    public static boolean isNotBlank(String str) {
        return !StringTools.isBlank(str);
    }

    /**
     * 截取字符串并去除空格
     *
     * @param str   需要截取的字符串
     * @param split 分隔符
     * @return 字符串数组
     */
    public static String[] splitString(String str, String split) {
        if (isBlank(str)) {
            return new String[0];
        } else {
            String arr[] = str.split(split);
            for (int i = 0; i < arr.length; i++) {
                arr[i] = arr[i].trim();
            }
            return arr;
        }
    }

    /**
     * 将id截取并去除空格，舍弃值为空的数组元素，分隔符为分号即：";"
     *
     * @param str   需要截取的字符串
     * @param split 分隔符
     * @return 字符串数组
     */
    public static List<String> getStrToList(String str, String split) {
        String arr[] = StringTools.splitString(str, split);
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < arr.length; i++) {
            if (isNotBlank(arr[i])) {
                list.add(arr[i].trim());
            }
        }
        return list;
    }

    /**
     * 将id截取并去除空格，舍弃值为空的数组元素，分隔符为分号即：";"
     *
     * @param str 需要截取的字符串
     * @return 字符串数组
     */
    public static List<String> getIds(String str) {
        return getStrToList(str, SPLIT_DEFAULT);
    }

    public static List<String> getIds(String str, String spilt) {
        return getStrToList(str, spilt);
    }

    /**
     * 合成IDs
     */
    public static String combIdsByDefault(List<String> list) {
        String ret = BLANK_STRING;
        for (String str : list) {
            if (isNotBlank(str)) {
                ret += (str + SPLIT_DEFAULT);
            }
        }
        if (ret.endsWith(SPLIT_DEFAULT)) {
            ret = ret.substring(0, ret.length() - 1);
        }
        return ret;
    }

    /**
     * 将id截取并去除空格，舍弃值为空的数组元素，分隔符为分号即：";"
     *
     * @param str 需要截取的字符串
     * @return 字符串数组
     */
    public static String[] getIdsToArray(String str) {
        return splitString(str, SPLIT_DEFAULT);
    }

    /**
     * 去掉重复的值<br>
     * 实现原理：return new ArrayList<T>(new HashSet<T>(list));
     */
    public static <T> List<T> getDifferentListVal(List<T> list) {
        return new ArrayList<T>(new HashSet<T>(list));
    }

    /**
     * 去掉重复的值
     */
    public static <T> T[] getDifferentArrayVal(T[] array) {
        List<T> list = new ArrayList<T>();
        for (int i = 0; i < array.length; i++) {
            if (!list.contains(array[i])) {
                list.add((T) array[i]);
            }
        }
        return (T[]) list.toArray((T[]) Array.newInstance(array.getClass()
                .getComponentType(), list.size()));
    }

    /**
     * 去掉字符串最后的0 如：152800 >> 1528
     *
     * @param str
     * @return
     */
    public static String removeStrLastZero(String str) {
        if (!str.substring(str.length() - 1).equals("0")) {
            return str;
        } else {
            return removeStrLastZero(str.substring(0, str.length() - 1));
        }
    }

    /**
     * 左填充字符串
     *
     * @param res    需要被填充的字符串
     * @param length 填充后的字符串长度，如果长度比输入的字符串短，则将原字符串从左向右截取length长度
     * @param pad    填充字符串，默认为空格即" "
     */
    public static String lpad(String res, int length, String pad) {
        StringBuffer ret = new StringBuffer();
        if (length < res.length()) {// 如果小于原长度，则截取新长度
            ret.append(res.substring(0, length));
        } else if (length == res.length()) {// 如果相等，则直接返回
            ret.append(res);
        } else {// 否则大于，则进行补位操作
            int needlen = length - res.length();// 计算需要补多少位
            if (isBlank(pad)) {// 如果为空或者null，直接用空格即" "补位
                for (int i = 0; i < needlen; i++) {
                    ret.append(" ");
                }
                ret.append(res);
            } else {// 如果不为空则用给出的参数进行补位
                int roll = needlen / pad.length();// 计算出需要完成循环补位字符串的循环次数
                String tail = pad.substring(0, needlen % pad.length());// 计算出需要补位的余数位的值
                for (int i = 0; i < roll; i++) {
                    ret.append(pad);
                }
                ret.append(tail).append(res);
            }
        }
        return ret.toString();
    }

    /**
     * 右填充字符串
     *
     * @param res    需要被填充的字符串
     * @param length 填充后的字符串长度，如果长度比输入的字符串短，则将原字符串从左向右截取length长度
     * @param pad    填充字符串，默认为空格即" "
     */
    public static String rpad(String res, int length, String pad) {
        StringBuffer ret = new StringBuffer();
        if (length < res.length()) {// 如果小于原长度，则截取新长度
            ret.append(res.substring(0, length));
        } else if (length == res.length()) {// 如果相等，则直接返回
            ret.append(res);
        } else {// 否则大于，则进行补位操作
            int needlen = length - res.length();// 计算需要补多少位
            if (isBlank(pad)) {// 如果为空或者null，直接用空格即" "补位
                for (int i = 0; i < needlen; i++) {
                    ret.append(" ");
                }
                ret = new StringBuffer(res).append(ret);
            } else {// 如果不为空则用给出的参数进行补位
                int roll = needlen / pad.length();// 计算出需要完成循环补位字符串的循环次数
                String tail = pad.substring(0, needlen % pad.length());// 计算出需要补位的余数位的长度
                for (int i = 0; i < roll; i++) {
                    ret.append(pad);
                }
                ret = new StringBuffer(res).append(ret).append(tail);
            }
        }
        return ret.toString();
    }

    /**
     * 比较两个字符串（数字型字符串）a <= b吗？
     */
    public static boolean lessThenAndEqualsTo(String a, String b) {
        return Integer.parseInt(a) <= Integer.parseInt(b);
    }

    /**
     * 比较两个字符串（数字型字符串）a >= b吗？
     */
    public static boolean greaterThenAndEqualsTo(String a, String b) {
        return Integer.parseInt(a) >= Integer.parseInt(b);
    }

    /**
     * 比较两个字符串（数字型字符串）a > b吗？
     */
    public static boolean greaterThen(String a, String b) {
        return Integer.parseInt(a) > Integer.parseInt(b);
    }


    /**
     * 获取大写字母编号，例如ABCDE...YZ，最小值为1，最大值为26，如果超出范围，则返回null
     */
    public static String getUpperNumLetter(int index) {
        if (index < 1 || index > 26) {
            return null;
        }
        // 开始于64，A=65
        int tag = 64 + index;
        return ((char) tag) + "";
    }

    /**
     * 生成UUID序列号
     */
    public static String getUUID() {
        return UUID.randomUUID().toString();
    }

    /**
     * 生成UUID序列号
     */
    public static String getUUID32() {
        return UUID.randomUUID().toString().replace("-", "");
    }


    /**
     * 编码转换
     *
     * @param res      需要转码的字符串
     * @param fromCode 字符串原始编码
     * @param toCode   需要转换到的编码
     * @return 转换后的字符串
     */
    public static String codeConversion(String res, String fromCode,
                                        String toCode) {
        try {
            return new String(res.getBytes(fromCode), toCode);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 转换URL编码格式
     */
    public static String urlEncode(String res) {
        String ret = res;
        try {
            ret = URLEncoder.encode(res, UTF8);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return ret;
    }

    /**
     * 转换URL反码格式
     */
    public static String urlDecode(String res) {
        String ret = res;
        try {
            ret = URLDecoder.decode(res, UTF8);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return ret;
    }

    /**
     * 获取扩展名
     */
    public static String getExtName(String filename) {
        int px = filename.lastIndexOf(".");
        if (px == -1) {// 未找到扩展名
            return "";
        } else {
            return filename.substring(px + 1, filename.length());
        }
    }

    /**
     * 过滤url的 域名与地址部分
     */
    public static String getHostPort(String str) {
        StringBuffer ipport = new StringBuffer();
        try {
            URL url = new URL(str);
            // 添加协议名
            ipport.append(url.getProtocol()).append("://");
            // 添加ip地址
            ipport.append(url.getHost());
            // 添加端口
            int port = url.getPort();
            if (port == -1) {
                port = url.getDefaultPort();
            }
            ipport.append(":").append(port);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return ipport.toString();
    }

    /**
     * 16进制转byte数组
     */
    public static byte[] hexToByte(String strhex) {
        if (strhex == null) {
            return null;
        }
        int l = strhex.length();
        if (l % 2 == 1) {
            return null;
        }
        byte[] b = new byte[l / 2];
        for (int i = 0; i != l / 2; i++) {
            b[i] = (byte) Integer.parseInt(strhex.substring(i * 2, i * 2 + 2),
                    16);
        }
        return b;
    }

    /**
     * 数组转16进制
     */
    private static String byteToHex(byte[] b) {
        StringBuffer sb = new StringBuffer(b.length);
        String sTemp;
        for (int i = 0; i < b.length; i++) {
            sTemp = Integer.toHexString(0xFF & b[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 字符转数字
     */
    public static int stringToNumber(String str) {
        if (isNotBlank(str)) {
            char[] chs = str.toCharArray();
            String val = BLANK_STRING;
            for (int i = 0; i < chs.length; i++) {
                int a = chs[i];
                val += (a);
            }
            return Integer.parseInt(val);
        } else {
            return 0;
        }
    }


    private static final String STR_MD5 = "MD5";

    /**
     * 读取文件的md5值
     */
    public static String getMd5ByFile(File file) {
        String value = null;
        FileInputStream inputStream = null;
        try {
            MessageDigest digest = MessageDigest.getInstance(STR_MD5);
            inputStream = new FileInputStream(file);
            byte[] buffer = new byte[4096];
            int len;
            while ((len = inputStream.read(buffer)) != -1) {
                digest.update(buffer, 0, len);
            }
            value = byteToHex(digest.digest());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return value;
    }

    /**
     * base64解码
     */
    public static byte[] getBase64Decode(String str) {
        if (null != str) {
            Base64.Decoder decoder = Base64.getDecoder();
            return decoder.decode(str.getBytes());
        }
        return null;
    }

    /**
     * base64编码
     */
    public static String getBase64Encode(byte[] array) {
        Base64.Encoder encoder = Base64.getEncoder();
        return new String(encoder.encode(array));
    }


    /**
     * 获取Xml元素中的元素名称
     */
    public static String getXmlElementText(String xml, String elementName) {
        Pattern pattern = Pattern.compile("<" + elementName + ".*?>([\\S\\s]*?)</" + elementName + ">",
                Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
        Matcher matcher = pattern.matcher(xml);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return "";
    }

    /**
     * 获取当前classpath路径
     */
    public static String getClassPath(String uri) {
        URL url = ClassLoader.getSystemResource(uri);
        if (null == url) {
            url = StringTools.class.getResource(uri);
            if (null == url) {
                url = StringTools.class.getClassLoader().getResource(uri);
                if (null == url) {
                    url = Thread.currentThread().getContextClassLoader().getResource(uri);
                    if (null == url) {
                        return null;
                    }
                }
            }
        }
        return new File(url.getPath()).getAbsolutePath();
    }

    /**
     * 判断文件的编码类型，默认返回GBK
     */
    @Deprecated
    public static String fileCode(String file) {
        try (BufferedInputStream stream = new BufferedInputStream(new FileInputStream(file))) {
            byte[] headb = new byte[3];
            stream.read(headb);
            String code = "GBK";
            if ((headb[0] == -1 && headb[1] == -2) || (headb[0] == -17 && headb[1] == -69 && headb[2] == -65)) {
                code = "UTF-8";// 此处应为UTF-16
            } else if (headb[0] == -2 && headb[1] == -1) {
                code = "Unicode";
            }
            return code;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 替换模板对象
     */
    public static StringBuilder replaceTempParams(String beanName, Map<String, String> params, StringBuilder tempBuilder) {
        for (Map.Entry<String, String> entry : params.entrySet()) {
            String key = entry.getKey();
            String val = entry.getValue();
            //合成要替换的表达式
            String tagkey = "${" + beanName + "." + key + "}";
            //计算表达式是否存在html中
            int tagidx = tempBuilder.indexOf(tagkey);
            //表达式存在
            if (tagidx >= 0) {
                //替换，从index开始到结束
                tempBuilder.replace(tagidx, tagidx + tagkey.length(), val);
            }
        }
        return tempBuilder;
    }


    /**
     * 判断是否为中文字符
     *
     * @param c
     * @return
     */
    public static boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
            return true;
        }
        return false;
    }


    /**
     * 字符串转unicode
     *
     * @param str
     * @return
     */
    public static String stringToUnicode(String str) {
        StringBuffer sb = new StringBuffer();
        char[] c = str.toCharArray();
        for (int i = 0; i < c.length; i++) {
            sb.append("\\u" + Integer.toHexString(c[i]));
        }
        return sb.toString();
    }

    /**
     * unicode转字符串
     *
     * @param unicode
     * @return
     */
    public static String unicodeToString(String unicode) {
        StringBuffer sb = new StringBuffer();
        String[] hex = unicode.split("\\\\u");
        for (int i = 1; i < hex.length; i++) {
            int index = Integer.parseInt(hex[i], 16);
            sb.append((char) index);
        }
        return sb.toString();
    }


    /**
     * 判断字符串是否为中文
     *
     * @param str
     * @return
     */
    public static boolean isChinese(String str) {
        List<String> matchlist = new ArrayList<>();
        matchlist.add("[\\u4e00-\\u9fa5]+");//基本汉字
        matchlist.add("[\\u9FA6-\\u9FCB]+");//汉字补充字
        matchlist.add("[\\u3400-\\u4DB5]+");//扩展A
//        matchlist.add("[\\u20000-\\u2A6D6]+");//扩展B
//        matchlist.add("[\\u2A700-\\u2B734]+");//扩展C
//        matchlist.add("[\\u2A700-\\u2B734]+");//扩展D
        matchlist.add("[\\u2F00-\\u2FD5]+");//康熙部首
        matchlist.add("[\\u2E80-\\u2EF3]+");//部首扩展
        matchlist.add("[\\uF900-\\uFAD9]+");//兼容汉字
//        matchlist.add("[\\u2F800-\\u2FA1D]+");//兼容扩展
        matchlist.add("[\\uE815-\\uE86F]+");//PUA(GBK)部件
        matchlist.add("[\\uE400-\\uE5E8]+");//部件扩展
        matchlist.add("[\\uE600-\\uE6CF]+");//PUA增补
        matchlist.add("[\\u31C0-\\u31E3]+");//汉字笔画
        matchlist.add("[\\u2FF0-\\u2FFB]+");//汉字结构
        matchlist.add("[\\u3105-\\u3120]+");//汉字结构
        matchlist.add("[\\u31A0-\\u31BA]+");//注音扩展
        matchlist.add("[〇]+");//〇
        for (String regex : matchlist) {
            if (RegularTools.match(regex, str)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取对象的字符串值，如果为空则返回null
     */
    public static String getStringByObject(Object obj) {
        return null == obj ? null : obj.toString();
    }


    /**
     * 合并字符串数组
     */
    public static String combListToString(List<String> list, String split) {
        return combListToString(list, BLANK_STRING, BLANK_STRING, split);
    }

    /**
     * 合并字符串数组
     */
    public static String combListToString(List<String> list, String begintag, String endtag, String split) {
        if (null != list) {
            String[] array = list.toArray(new String[list.size()]);
            return combListToString(array, begintag, endtag, split);
        }
        return null;
    }

    /**
     * 合并字符串数组
     */
    public static String combListToString(String[] list, String begintag, String endtag, String split) {
        if (null != list && list.length > 0) {
            StringBuilder builder = new StringBuilder();
            for (String str : list) {
                builder.append(begintag).append(str).append(endtag).append(split);
            }
            return builder.substring(0, builder.length() - split.length());
        }
        return BLANK_STRING;
    }


    public static double roundUp(Object num, int pointlen) {
        BigDecimal bg = null;
        if (num instanceof Double) {
            bg = new BigDecimal((double) num);
        } else if (num instanceof Long) {
            bg = new BigDecimal((long) num);
        } else {
            bg = new BigDecimal(num.toString());
        }
        return bg.setScale(pointlen, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**保留小数点N位，请勿输入0*/
    public static double roundMid(Object num, int pointlen) {
        BigDecimal bg = null;
        if (num instanceof Double) {
            bg = new BigDecimal((double) num);
        } else if (num instanceof Long) {
            bg = new BigDecimal((long) num);
        } else {
            bg = new BigDecimal(num.toString());
        }
        return bg.setScale(pointlen, BigDecimal.ROUND_DOWN).doubleValue();
    }
    
    /**
     * 获取class的路径
     */
    public static String getClassPath() {
        String path = StringTools.class.getProtectionDomain().getCodeSource().getLocation().getFile();
        // 去掉开头的斜杠
        if (path.startsWith("/") || path.startsWith("\\")) {
            path = path.substring(1);
        }

        String classesstr = "classes" + File.separator;

        // 如果是jar包
        if (path.endsWith("jar")) {
            File filepath = new File(path);
            path = filepath.getParent();
        } else if (path.endsWith(classesstr)) {
            // 如果是开发环境
            path = path.substring(0, path.lastIndexOf(classesstr));
        }
        //url解码
        if (StringTools.isNotBlank(path) && path.contains("%")) {
            path = StringTools.urlDecode(path);
        }

        return path;
    }


    // ======================测试======================
    public static void main(String[] args) {

        System.out.println(getClassPath("/"));
        System.out.println(getClassPath());
    }

}
